/**
 * 
 */
package br.ufal.ic.sd.project.logbyk.core;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

import br.ufal.ic.sd.project.exception.LogByKException;

/**
 * @author keven andrade
 *
 */
public class Channel {
    
    	private String strIP;
	private int intPort;
	
	private Socket objSocket;
	private ObjectInputStream objInputStreamIn;
	private ObjectOutputStream objOutputStreamOut;
	
	/**
	 * Client Channel's Constructor 
	 * @param strIP
	 * @param intPort
	 * @throws LogByKException 
	 */
	public Channel(String strIP, int intPort) throws LogByKException{
		
		this.strIP = strIP;
		this.intPort = intPort;
		
		try {
			this.objSocket = new Socket(this.getStrIP(), this.getIntPort());
			
			this.objOutputStreamOut = new ObjectOutputStream(this.objSocket.getOutputStream());
			this.objOutputStreamOut.flush();
			this.objInputStreamIn = new ObjectInputStream(this.objSocket.getInputStream());
			
			System.out.println("Connection Open");
			
		} catch (UnknownHostException e) {
//			System.err.println("Connect Failed on host " + this.getStrIP());
		    throw new LogByKException(e.getMessage());
		} catch (IOException e) {
			System.err.println("Connect Failed on host " + this.getStrIP());
			 throw new LogByKException(e.getMessage());
		}
	}
	
	public Channel(Socket objSocket) throws LogByKException{
	        
	    
	    	try {
		
        		this.objSocket = objSocket;
        		
        		this.objOutputStreamOut = new ObjectOutputStream(this.objSocket.getOutputStream());
        		this.objOutputStreamOut.flush();
        		this.objInputStreamIn = new ObjectInputStream(this.objSocket.getInputStream());
        		
        		System.out.println("Connection Open");
		
        	} catch (UnknownHostException e) {
//        		System.err.println("Connect Failed on host " + this.getStrIP());
        	    throw new LogByKException(e.getMessage());
        	} catch (IOException e) {
//        		System.err.println("Connect Failed on host " + this.getStrIP());
        	    throw new LogByKException(e.getMessage());
        	}
	    
	}
	
	//=========================================================== Getters and Setters =========================================================

	
	public Channel() {
	}

	/**
	 * @return the strIP
	 */
	public String getStrIP() {
	    return strIP;
	}

	/**
	 * @param strIP the strIP to set
	 */
	public void setStrIP(String strIP) {
	    this.strIP = strIP;
	}

	/**
	 * @return the intPort
	 */
	public int getIntPort() {
	    return intPort;
	}

	/**
	 * @param intPort the intPort to set
	 */
	public void setIntPort(int intPort) {
	    this.intPort = intPort;
	}

	/**
	 * @return the objSocket
	 */
	public Socket getObjSocket() {
	    return objSocket;
	}

	/**
	 * @param objSocket the objSocket to set
	 */
	public void setObjSocket(Socket objSocket) {
	    this.objSocket = objSocket;
	}

	/**
	 * @return the objInputStreamIn
	 */
	public ObjectInputStream getObjInputStreamIn() {
	    return objInputStreamIn;
	}

	/**
	 * @param objInputStreamIn the objInputStreamIn to set
	 */
	public void setObjInputStreamIn(ObjectInputStream objInputStreamIn) {
	    this.objInputStreamIn = objInputStreamIn;
	}

	/**
	 * @return the objOutputStreamOut
	 */
	public ObjectOutputStream getObjOutputStreamOut() {
	    return objOutputStreamOut;
	}

	/**
	 * @param objOutputStreamOut the objOutputStreamOut to set
	 */
	public void setObjOutputStreamOut(ObjectOutputStream objOutputStreamOut) {
	    this.objOutputStreamOut = objOutputStreamOut;
	}
	
	//================================================================== Methods ===============================================================
	
	/**
	 * Open a connection with an particular server  using the server IP and the port
	 */
	public void connect() {
		
		try {
			this.objSocket = new Socket(this.getStrIP(), this.getIntPort());
			
			this.objOutputStreamOut = new ObjectOutputStream(this.objSocket.getOutputStream());
			this.objOutputStreamOut.flush();
			this.objInputStreamIn = new ObjectInputStream(this.objSocket.getInputStream());
			
			System.out.println("Connection Open");
			
		} catch (UnknownHostException e) {
			System.err.println("Connect Failed on host " + this.getStrIP());
		} catch (IOException e) {
			System.err.println("Connect Failed on host " + this.getStrIP());
		}
	}
	
	/**
	 * Close an existing connection with a server
	 */
	public void closeConnection() {
	    
	    try {
		this.objSocket.close();
	    } catch (IOException e) {
		System.err.println("System failed!");
		
	    }
	    
	    System.out.println("Connection closed");
	}
	
	/**
	 *@Method that read a sequence of bytes from the channel
	 */
	public int read(byte[] bytes) {
		
		try {
			
			return this.objInputStreamIn.read(bytes);

		} catch (IOException e) {
//		    System.err.println(e.getMessage());
		    System.err.println("Ooops!");
		}

		return -1;
	}

	/**
	 *@Method that read an object from the channel
	 *@return null in case of error
	 */
	public String read() {
		
		try {
			return this.objInputStreamIn.readObject().toString();
			
		} catch (IOException e) {
//		    System.err.println(e.getMessage());
		    System.err.println("Ooops!");
		} catch (ClassNotFoundException e) {
//		    System.err.println(e.getMessage());
		    System.err.println("Ooops!");
		}
		return null;
	}

	/**
	 *@Method that push a sequence bytes throw the communication channel 
	 */
	public void write(byte[] bytes, int i) {

		try {
			
			this.objOutputStreamOut.write(bytes, 0, i);
			this.objOutputStreamOut.flush();

		} catch (IOException e) {
//		    System.err.println(e.getMessage());
		    System.err.println("Ooops!");
		}
	}

	/**
	 *@Method that push a message throw the communication channel 
	 */
	public void write(String strMessage) {

		try {
			this.objOutputStreamOut.writeObject(strMessage);
			this.objOutputStreamOut.flush();

		} catch (IOException e) {
//		    System.err.println(e.getMessage());
		    System.err.println("Ooops!");
		}
	}

	public void close() throws LogByKException {
	    
	    try {
		this.objSocket.close();
	    } catch (IOException e) {
//		e.printStackTrace();
		throw new LogByKException(e.getMessage());
	    }
	    
	}
}
